//
//  KiaController.m
//  Kia
//
//  Created by Andrey on 03/02/2009.
//  Copyright 2009 Karma Software. All rights reserved.
//

#import "KiaController.h"
#import "KiaView.h"
#import "HistogramView.h"
#import "ChromaticityHistogramView.h"
#import "KImageSegmentator.h"
#import "KSimpleFloatArray.h"
#import "KSimpleUIntArray.h"
#import "KSegmentationMap.h"
#import "KCurve.h"
#import "CurvesView.h"
#import "KHistogram.h"
#import "KCumulativeHistogram.h"
#import "K2DHistrogram.h"
#import "KImageLayer.h"
#import "KImageAutoEnhancer.h"

/* KFilter arch support */
#import "KFilterOperation.h"
#import "KFilterChainOperation.h"
#import "KAutoLevels.h"
#import "KCurves.h"
#import "KEqualizeChannel.h"
#import "KDesaturate.h"
#import "KFadeFilter.h"
#import "KSegmentFilter.h"
#import "KImageProcessor.h"

@implementation KiaController

@synthesize currentImage;
@synthesize displayBuffer;
@synthesize segmentationMap;
@synthesize chromaticitySegmentationMap;
@synthesize channelForSegmentation;
@synthesize useChromaticityForSegmentation;
@synthesize displayedHistogramChannel;
@synthesize displayedChromaticityHistogramChannel;
@synthesize displayCumulativeHistogram;
@synthesize histogramWindow;

- (id) init
{
	if (self = [super init])
	{
		currentImage = nil;
		displayBuffer = nil;
		displayedHistogramChannel = BrightnessImageChannel;
		displayCumulativeHistogram = NO;
		useChromaticityForSegmentation = YES;
	}
	
	return self;
}

- (void) updateViews
{
	[self updateHistogramStatistics];
	[chromaticityHistogramView setNeedsDisplay:YES];
	[histogramView setNeedsDisplay:YES];
	[mainView setNeedsDisplay:YES];
}

- (void) currentImageChangedAndNeedsDisplay: (Boolean)needsDisplay
{
	[currentImage syncWithPixels];
	[self updateViews];
}

- (IBAction) openDocument: (id)sender  
{
	[[NSOpenPanel openPanel] 
	 beginSheetForDirectory:nil
	 file:nil
	 modalForWindow:window 
	 modalDelegate:self
	 didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:)
	 contextInfo:NULL];  
}

- (IBAction) saveDocument: (id) sender
{
	[[NSSavePanel savePanel]
	 beginSheetForDirectory:nil
	 file:nil
	 modalForWindow:window 
	 modalDelegate:self
	 didEndSelector:@selector(savePanelDidEnd:returnCode:contextInfo:)
	 contextInfo:NULL];  
}

- (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void*)contextInfo
{
	if (returnCode == NSOKButton)
	{
		if (currentImage != nil)
			[currentImage release];
		
		currentImage = [[[KImage alloc] initWithFile:[sheet filename]] retain];
		[self backupCurrentImage];
		displayBuffer = currentImage;
		
		NSSize currentImageSize = NSMakeSize([currentImage width], [currentImage height]);
		NSPoint newWindowOrigin = 
		NSMakePoint([window frame].origin.x - (currentImageSize.width - [window frame].size.width),
					[window frame].origin.y - (currentImageSize.height - [window frame].size.height));
		NSRect newWindowFrame = 
		NSMakeRect(newWindowOrigin.x, newWindowOrigin.y, currentImageSize.width, currentImageSize.height);
		
		[window setFrame: newWindowFrame display: YES animate: YES];
		[window setMaxSize: currentImageSize];
		
		[mainView setFrameSize: currentImageSize];
		[mainView setBoundsSize: currentImageSize];
		
		[chromaticitySegmentationMap release]; chromaticitySegmentationMap = nil;
		[layersTableView reloadData];
		[activeSegmentComboBox removeAllItems];
		[activeSegmentComboBox addItemWithObjectValue:@"Global"];
		[self updateHistogramStatistics];
		NSLog(@"%@", currentImage);
		
		[mainView setNeedsDisplay: YES];
		[histogramView setNeedsDisplay:YES];
		[chromaticityHistogramView setNeedsDisplay:YES];
	}
}

- (void)savePanelDidEnd:(NSSavePanel *)sheet returnCode:(int)returnCode contextInfo:(void*)contextInfo
{
	if (returnCode == NSOKButton)
	{
		NSData* tiffData = [displayBuffer.bitmap TIFFRepresentation];
		[tiffData writeToFile:[sheet filename] atomically:NO];
	}
}

- (void) backupCurrentImage
{
	NSData* tiffData = [currentImage.bitmap TIFFRepresentation];
	NSString* backupFilename = 
	[NSTemporaryDirectory() stringByAppendingString:@"CurrentImageBackup.tiff"];
	
	[tiffData writeToFile:backupFilename atomically:NO];
}

- (IBAction) restoreCurrentImageFromBackup: (id) sender
{
	NSString* backupFilename = 
	[NSTemporaryDirectory() stringByAppendingString:@"CurrentImageBackup.tiff"];
	[currentImage restoreFromBackup:backupFilename];
	
	[currentImage recomputeStatistics];
	[self updateHistogramStatistics];
	
	displayBuffer = currentImage;
	[histogramView setNeedsDisplay:YES];
	[chromaticityHistogramView setNeedsDisplay:YES];
	[mainView setNeedsDisplay:YES];
}

- (IBAction) showHistogramWindow: (id) sender
{	
	[histogramChannelComboBox selectItemAtIndex:BrightnessImageChannel];
	displayedHistogramChannel = [histogramChannelComboBox indexOfSelectedItem];
	
	[self updateHistogramStatistics];
	
	[histogramView setNeedsDisplay:YES];
	[histogramWindow makeKeyAndOrderFront:histogramWindow];
}

- (IBAction) changeDisplayedHistogramChannel: (id) sender
{
	displayedHistogramChannel = [histogramChannelComboBox indexOfSelectedItem];
	
	[self updateHistogramStatistics];
	
	[histogramView setNeedsDisplay:YES];
}

- (void) updateHistogramStatistics
{
	if (currentImage != nil)
	{
		CGFloat channelMean = 
		currentImage.histogram.channelHistograms[displayedHistogramChannel].mean;
		[meanLabel setFloatValue:channelMean];
		
		CGFloat channelVariance = 
		currentImage.histogram.channelHistograms[displayedHistogramChannel].variance;
		[varianceLabel setFloatValue:channelVariance];
		
		CGFloat channelUniformity =
		currentImage.histogram.channelHistograms[displayedHistogramChannel].uniformity;
		[uniformityLabel setFloatValue:channelUniformity];
	}
}

- (IBAction) changeHistogramType: (id) sender
{
	if (displayCumulativeHistogram == NO)
		displayCumulativeHistogram = YES;
	else
		displayCumulativeHistogram = NO;
	
	[histogramView setNeedsDisplay:YES];
}

- (void) trackMouseDragging: (NSPoint)mouseLocation inView: (NSView*)view
{
	if (view == mainView)
	{
		mouseLocation.y = currentImage.height - mouseLocation.y;
		KRedundantColorVector colorVector = 
		[[displayBuffer pixelAtX:mouseLocation.x y:mouseLocation.y] redundantColorVector];
		
		CGFloat component = colorVector.data[displayedHistogramChannel];
		
		NSUInteger highlightedInterval = 
		(NSUInteger)(component * currentImage.histogram.intervalCount);
		
		if (highlightedInterval == currentImage.histogram.intervalCount)
			highlightedInterval == currentImage.histogram.intervalCount - 1;
		
		histogramView.highlightedInterval = highlightedInterval;
		[histogramView setNeedsDisplay:YES];
		
		// Chromaticity histogram tracking
		chromaticityHistogramView.highlightedCell = 
		NSMakePoint(256 * colorVector.data[AImageChannel], 
					(1 - colorVector.data[BImageChannel]) * 256);
		[chromaticityHistogramView setNeedsDisplay:YES];
		
		curvesView.highlightedLevel = 
		colorVector.data[[affectedChannelComboBox indexOfSelectedItem]];
		[curvesView setNeedsDisplay:YES];
	}
	else if (view == curvesView)
	{
		[curvesView.curve 
		 replaceControlPointAt:curvesView.selectedCurveControlPoint 
		 withPoint:mouseLocation];
		[curvesView setNeedsDisplay:YES];
	}
}

- (void) mouseClicked: (NSPoint)mouseLocation
{
	mouseLocation.y = currentImage.height - mouseLocation.y;
	KRedundantColorVector colorVector = 
	[[displayBuffer pixelAtX:mouseLocation.x y:mouseLocation.y] redundantColorVector];
	
	CGFloat channelValue = colorVector.data[[affectedChannelComboBox indexOfSelectedItem]];
	[curvesView.curve addControlPoint:
	NSMakePoint(channelValue, [curvesView.curve valueAt:channelValue])];
	[curvesView setNeedsDisplay:YES];
}


- (IBAction) showSegmentationWindow: (id) sender
{
	[channelForSegmentationComboBox selectItemAtIndex:0];
	[segmentationWindow makeKeyAndOrderFront:self];
}

- (void) processCurrentImageUsingFilter: (KFilter*)filter
{
	[self backupCurrentImage];
	
	KFilterOperation* filterOperation = 
	[[KFilterOperation alloc] initWithImage:currentImage andFilter:filter];
	
	[filterOperation addObserver:self forKeyPath:@"isFinished" options:0 context:NULL];
	[filterOperation start];
	[filterOperation release];
}

- (void) processCurrentImageUsingFilterChain: (NSArray*)filterChain
{
	[self backupCurrentImage];
	
	KFilterChainOperation* filterChainOperation =
	[[KFilterChainOperation alloc] initWithImage:currentImage andFilterChain:filterChain];
	
	[filterChainOperation addObserver:self forKeyPath:@"isFinished" options:0 context:NULL];
	[filterChainOperation start];
	[filterChainOperation release];
}

- (void)observeValueForKeyPath:(NSString*)keyPath 
					  ofObject:(id)object
						change:(NSDictionary*)change
					   context:(void*)context
{
	[self currentImageChangedAndNeedsDisplay:YES];
}

- (IBAction) changeSegmentationSource: (id) sender
{
	[channelForSegmentationComboBox setEnabled:useChromaticityForSegmentation];
	
	useChromaticityForSegmentation = !useChromaticityForSegmentation;
}

- (IBAction) segmentImage: (id) sender
{
	[segmentationWindow orderOut:self];
/*	
	NSColor* rainbow[] = 
	{
		[NSColor brownColor],
		[NSColor blueColor],
		[NSColor cyanColor],
		[NSColor greenColor],
		[NSColor magentaColor],
		[NSColor yellowColor],
		[NSColor orangeColor]
	};
*/	
	NSUInteger segmentCount = [segmentCountTextField intValue];
	[activeSegmentComboBox removeAllItems];
	[activeSegmentComboBox addItemWithObjectValue:@"All"];
	for (int i = 0; i < segmentCount; i++)
	{
		[activeSegmentComboBox addItemWithObjectValue:[NSNumber numberWithInt:i]];
	}
	[activeSegmentComboBox selectItemAtIndex:0];
	
	if (useChromaticityForSegmentation == NO)
	{		
		channelForSegmentation = [channelForSegmentationComboBox indexOfSelectedItem];
		
		if (segmentationMap != nil)
			[segmentationMap release];
		
		segmentationMap = [[KImageSegmentator segmentImage:currentImage 
							  usingKMeansWithSegmentCount:segmentCount
										  andUsingChannel: channelForSegmentation] retain];
	}
	else
	{
		if (chromaticitySegmentationMap != nil)
			[chromaticitySegmentationMap release];
		
		chromaticitySegmentationMap = [[KImageSegmentator segmentImageByChromaticity:currentImage
														usingKMeansWithSegmentCount:segmentCount] retain];
		NSLog(@"Finished segmentation.");
		
		KImageLayer* background = [currentImage layerAtIndex:0];
		for (int i = 0; i < chromaticitySegmentationMap.segmentCount; i++)
		{
			KImageLayer* newSegmentationLayer = 
			[[KImageLayer alloc] initWithLayer:background 
									   andMask:chromaticitySegmentationMap.segments[i]];
			
			newSegmentationLayer.name = [NSString stringWithFormat:@"Segment #%d", i];
			
			[currentImage addLayerOnTop:newSegmentationLayer];
			NSLog(@"%d", [newSegmentationLayer retainCount]);
			[newSegmentationLayer release];
		}
		
		[self currentImageChangedAndNeedsDisplay:YES];
		[layersTableView reloadData];
 
	}
}

- (IBAction) showChromaticityHistogramWindow: (id) sender
{
	displayedChromaticityHistogramChannel = HueChromaticityChannel;
	[chromaticityHistogramChannelComboBox selectItemAtIndex:0];
	[chromaticityHistogramWindow makeKeyAndOrderFront:self];
}

- (IBAction) changeDisplayedChromaticityHistogramChannel: (id) sender
{
	displayedChromaticityHistogramChannel = 
	[chromaticityHistogramChannelComboBox indexOfSelectedItem];
	[chromaticityHistogramView setNeedsDisplay:YES];
}

/* Layers table */

- (id)tableView:(NSTableView *)aTableView 
objectValueForTableColumn:(NSTableColumn *)aTableColumn
			row:(int)rowIndex
{
	NSUInteger indexInCollection = [aTableView numberOfRows] - rowIndex - 1;
	KImageLayer* layer = 
	[currentImage layerAtIndex:indexInCollection];	
	NSUInteger columnIdentifier = [[aTableColumn identifier] intValue];
	
	if (columnIdentifier == 0)
	{
		return [NSNumber numberWithBool:layer.visible];
	}
	else if (columnIdentifier == 1)
	{
		return layer.name;
	}
	else if (columnIdentifier == 2)
	{
		return [NSNumber numberWithFloat:layer.opacity];
	}
	else if (columnIdentifier == 3)
	{
		switch (layer.blendMode) {
			case NormalBlendMode:
				return @"Normal";
			case BrightnessBlendMode:
				return @"Brightness";
			default:
				break;
		}
	}
	
	return nil;
}

- (void)tableView:(NSTableView *)aTableView
   setObjectValue:(id)anObject
   forTableColumn:(NSTableColumn *)aTableColumn
			  row:(int)rowIndex
{
	NSUInteger indexInCollection = [aTableView numberOfRows] - rowIndex - 1;
	KImageLayer* layer = 
	[currentImage layerAtIndex:indexInCollection];
	NSUInteger columnIdentifier = [[aTableColumn identifier] intValue];
	
	if (columnIdentifier == 0)
	{
		layer.visible = [anObject boolValue];
	}
	else if (columnIdentifier == 1)
	{
		layer.name = anObject;
	}
	else if (columnIdentifier == 2)
	{
		layer.opacity = [anObject floatValue];
	}
	else if (columnIdentifier == 3)
	{		
		if ([anObject isEqualToString:@"Normal"])
			layer.blendMode = NormalBlendMode;
		else if ([anObject isEqualToString:@"Brightness"])
			layer.blendMode = BrightnessBlendMode;
	}
	
	[self currentImageChangedAndNeedsDisplay:YES];
}

- (int)numberOfRowsInTableView:(NSTableView *)aTableView
{
    return [currentImage layerCount];
}

- (void)selectedLayerDidChange
{
	/*
	NSUInteger indexInCollection = 
	[layersTableView numberOfRows] - [layersTableView selectedRow] - 1;
	currentImage.indexOfSelectedLayer = indexInCollection;
	 */
	currentImage.indexOfSelectedLayer = 0;
}

- (void)tableViewSelectionDidChange:(NSNotification *)aNotification
{
	[self selectedLayerDidChange];
}

/* Layers table END */

/* Layers window */

- (IBAction) showLayersWindow: (id) sender
{
	[layersWindow makeKeyAndOrderFront:self];
}

- (IBAction) addNewLayer: (id) sender
{
	KImageLayer* newBlankLayer = 
	[[KImageLayer alloc] initWithWidth:currentImage.width 
							 andHeight:currentImage.height];
	
	KPixel* blackPixel = [[KPixel alloc] initWithRed:0 green:0 andBlue:0];
	[newBlankLayer fillWithPixels:blackPixel];
	[blackPixel release];
	
	[currentImage addLayerOnTop:newBlankLayer];
	[newBlankLayer release];
	
	[self currentImageChangedAndNeedsDisplay:YES];
	[layersTableView reloadData];
}

- (IBAction) cloneCurrentLayerAndPutItOnTop: (id) sender
{
	KImageLayer* newLayer = 
	[[KImageLayer alloc] initWithLayer:
	 [currentImage layerAtIndex:currentImage.indexOfSelectedLayer]];
	
	[currentImage addLayerOnTop:newLayer];
	[newLayer release];
	
	[self currentImageChangedAndNeedsDisplay:YES];
	[layersTableView reloadData];
}

- (IBAction) removeSelectedLayer: (id) sender
{
	[currentImage removeLayerFromTop];
	[self currentImageChangedAndNeedsDisplay:YES];
	[layersTableView reloadData];
}

/* Layers window END */

/* Filters window */

- (IBAction) showFiltersWindow: (id) sender
{
	[activeSegmentComboBox selectItemAtIndex:0];
	[affectedChannelComboBox selectItemAtIndex:BrightnessImageChannel];
	[filtersWindow makeKeyAndOrderFront:self];
}

- (IBAction) applyFilter: (id) sender
{
	KFilter* filter;
	enum KFilterType selectedFilterType = 
	[[[filterParametersTabView selectedTabViewItem] identifier] intValue];
	NSNumber* affectedChannel = 
	[NSNumber numberWithUnsignedInteger:[affectedChannelComboBox indexOfSelectedItem]];
	
	switch (selectedFilterType)
	{
		case AutoLevels:
			{
				NSNumber* rejectionThresholdForShadows = 
				[NSNumber numberWithFloat:[rejectionThresholdForShadowsTextField floatValue]];
				NSNumber* rejectionThresholdForHighlights = 
				[NSNumber numberWithFloat:[rejectionThresholdForHighlightsTextField floatValue]];
				
				filter = [KAutoLevels filterWithParameters:
						  [NSDictionary dictionaryWithObjectsAndKeys:
						   rejectionThresholdForShadows, 
						   @"RejectionThresholdForShadows", 
						   rejectionThresholdForHighlights, 
						   @"RejectionThresholdForHighlights", 
						   affectedChannel,
						   @"AffectedChannel", nil
						  ]];
			}
			break;
		case Curves:
			{
				filter = [KCurves filterWithParameters:
						  [NSDictionary dictionaryWithObjectsAndKeys:
						   curvesView.curve, 
						   @"Curve", 
						   affectedChannel, 
						   @"AffectedChannel", nil
						  ]];
			}
			break;
		case Equalize:
			{
				filter = [KEqualizeChannel filterWithParameters:
						  [NSDictionary dictionaryWithObjectsAndKeys:
						   affectedChannel,
						   @"ChannelToEqualize", nil
						  ]];
			}
			break;
		case Desaturate:
			{
				KSimpleFloatArray* channelFractions = [KSimpleFloatArray arrayWithCapacity:3];
				channelFractions.data[RedImageChannel] = [redChannelFractionSlider floatValue];
				channelFractions.data[GreenImageChannel] = [greenChannelFractionSlider floatValue];
				channelFractions.data[BlueImageChannel] = [blueChannelFractionSlider floatValue];
								
				filter = [KDesaturate filterWithParameters:
						  [NSDictionary dictionaryWithObjectsAndKeys: 
						   channelFractions,
						   @"ChannelFractions", nil
						  ]];
			}
			break;

			
		default:
			break;
	}
	
	KFilter* intermediateFilter;
	
	NSNumber* fadeFraction = [NSNumber numberWithFloat:[fadeFilterSlider floatValue]];
	if ([fadeFraction floatValue] < 1.0 && [fadeFraction floatValue] > 0)
	{
		intermediateFilter = [KFadeFilter filterWithParameters:
							  [NSDictionary dictionaryWithObjectsAndKeys:
							   filter, 
							   @"Filter", 
							   fadeFraction, 
							   @"Fraction", nil
							   ]];
	}
	else if ([fadeFraction floatValue] == 1.0)
		intermediateFilter = filter;
	else if ([fadeFraction floatValue] == 0.0)
		return;
	
	KFilter* resultingFilter;
	if ([activeSegmentComboBox indexOfSelectedItem] != 0)
	{
		NSUInteger activeSegment = [activeSegmentComboBox intValue];
		resultingFilter = [KSegmentFilter filterWithParameters:
						   [NSDictionary dictionaryWithObjectsAndKeys:
							intermediateFilter, 
							@"Filter",
							chromaticitySegmentationMap.segments[activeSegment],
							@"Segment", nil
						   ]];
	}
	else
		resultingFilter = intermediateFilter;
		
	[self processCurrentImageUsingFilter:resultingFilter];
	NSLog(@"%@", currentImage);
}

- (IBAction) cancelAndCloseFiltersWindow: (id) sender
{
	[filtersWindow orderOut:self];
}
/* Filters window END */

- (IBAction) autoEnhanceCurrentImage: (id) sender
{
	[autoEnhancementWindow orderOut:self];
	NSUInteger importantSegment = [importantSegmentComboBox indexOfSelectedItem] - 1;
/*	
	if (importantSegment != -1)
	{
		KHistogram* segmentHistogram = [[KHistogram alloc] initWithIntervalCount:256 andChannelCount:IMAGE_CHANNEL_COUNT];
		[segmentHistogram computeForSegment:importantSegment ofImage:currentImage withCrispSegmentationMap:chromaticitySegmentationMap.crispSegmentationMap];
		[segmentHistogram saveToTikzFile:@"/Users/Andrey/MSTU/Master/Keynote/ImportantSegmentHistogramBefore.tex" forChannel:BrightnessImageChannel];
		[segmentHistogram release];
	}
*/		
	// Remove segment clues
	for (int i = 0; i < chromaticitySegmentationMap.segmentCount; i++)
	{
		[currentImage removeLayerFromTop];
	}
	[layersTableView reloadData];
	
	[self backupCurrentImage];
	KImageAutoEnhancer* autoEnhancer = [KImageAutoEnhancer autoEnhancerWithImage:currentImage andSegmentationMap:chromaticitySegmentationMap];
	[autoEnhancer autoEnhanceWithImportantSegment:importantSegment];
/*	
	if (importantSegment != -1)
	{
		KHistogram* segmentHistogram = [[KHistogram alloc] initWithIntervalCount:256 andChannelCount:IMAGE_CHANNEL_COUNT];
		[segmentHistogram computeForSegment:importantSegment ofImage:currentImage withCrispSegmentationMap:chromaticitySegmentationMap.crispSegmentationMap];
		[segmentHistogram saveToTikzFile:@"/Users/Andrey/MSTU/Master/Keynote/ImportantSegmentHistogramAfter.tex" forChannel:BrightnessImageChannel];
		[segmentHistogram release];
	}
*/
	[self updateViews];
}

- (IBAction) showAutoEnhancementWindow: (id) sender
{
	// (Re-)Initialize combo box
	[importantSegmentComboBox removeAllItems];
	[importantSegmentComboBox addItemWithObjectValue:@"All"];
	[importantSegmentComboBox selectItemAtIndex:0];
	
	if (chromaticitySegmentationMap != nil)
	{
		for (int i = 0; i < chromaticitySegmentationMap.segmentCount; i++)
		{
			[importantSegmentComboBox addItemWithObjectValue:[NSNumber numberWithInt:i]];
		}
	}
	
	[autoEnhancementWindow makeKeyAndOrderFront:self];
}

#define MULTIPLIER_STEP 0.25

- (IBAction) writeOutVariants: (id)sender
{
	NSString* sceneName = @"/Users/Andrey/Sites/Experiment/Variants/3GuysAndGirl";
		
	for (CGFloat u = 0.0; u <= 1.0; u += MULTIPLIER_STEP)
	{
		NSString* autoLevelsVariantFilename = 
		[sceneName stringByAppendingFormat:@"-AutoLevels-%3.2f", u];
		Boolean needCleanupAfterAutoLevels = [KImageProcessor processImage:currentImage
											  usingTemporaryLayerForFilter:
											  [KImageProcessor getDefaultFilterOfType:AutoLevels] 
															withMultiplier:u];
		
		[currentImage saveToTIFF:autoLevelsVariantFilename];
		NSLog(@"Written out variant %@.tiff", autoLevelsVariantFilename);
		
		for (CGFloat v = 0.0; v <= 1.0; v += MULTIPLIER_STEP)
		{
			NSString* curvesVariantFilename = [autoLevelsVariantFilename stringByAppendingFormat:@"-Curves-%3.2f", v];
			
			Boolean needCleanupAfterCurves = [KImageProcessor processImage:currentImage
											  usingTemporaryLayerForFilter:
											  [KImageProcessor getDefaultFilterOfType:Curves] 
															withMultiplier:v];
			
			[currentImage saveToTIFF:curvesVariantFilename];
			NSLog(@"Written out variant %@.tiff", curvesVariantFilename);
			
			for (CGFloat w = 0.0;  w <= 1.0; w += MULTIPLIER_STEP) 
			{
				NSString* equalizeVariantFilename = [curvesVariantFilename stringByAppendingFormat:@"-Equalize-%3.2f", w];
				
				Boolean needCleanupAfterEqualize = [KImageProcessor processImage:currentImage
													usingTemporaryLayerForFilter:
													[KImageProcessor getDefaultFilterOfType:Equalize] 
																  withMultiplier:w]; 
			
				[currentImage saveToTIFF:equalizeVariantFilename];
				NSLog(@"Written out variant %@.tiff", equalizeVariantFilename);
				
				if (needCleanupAfterEqualize)
					[currentImage removeLayerFromTop];
				
				[currentImage recomputeStatistics];
			}
			
			if (needCleanupAfterCurves)
				[currentImage removeLayerFromTop];
			
			[currentImage recomputeStatistics];
		}
		
		if (needCleanupAfterAutoLevels)
			[currentImage removeLayerFromTop];
		
		[currentImage recomputeStatistics];
	}
}

- (void) dealloc
{
	[currentImage release];
	[segmentationMap release];
	
	[super dealloc];
}

@end
